Move print-editor to demos
authorMatthias Clasen <mclasen@redhat.com>
Tue, 12 May 2020 19:28:34 +0000 (15:28 -0400)
committerMatthias Clasen <mclasen@redhat.com>
Tue, 12 May 2020 19:43:43 +0000 (15:43 -0400)
It is too demo-esque for languishing in tests/.

demos/meson.build
demos/print-editor/meson.build [new file with mode: 0644]
demos/print-editor/print-editor.c [new file with mode: 0644]
tests/meson.build
tests/print-editor.c [deleted file]

index c9d7081b404dde287dceff7c01c163d931223b45..42590368a23f2a6e014bd1fffc8757f5395f281e 100644 (file)
@@ -3,3 +3,4 @@ subdir('gtk-demo')
 subdir('icon-browser')
 subdir('node-editor')
 subdir('widget-factory')
+subdir('print-editor')
diff --git a/demos/print-editor/meson.build b/demos/print-editor/meson.build
new file mode 100644 (file)
index 0000000..cca25a9
--- /dev/null
@@ -0,0 +1,6 @@
+executable('print-editor',
+           ['print-editor.c'],
+           c_args: common_cflags,
+           dependencies: libgtk_dep,
+           include_directories: confinc,
+           gui_app: true)
diff --git a/demos/print-editor/print-editor.c b/demos/print-editor/print-editor.c
new file mode 100644 (file)
index 0000000..37ea8cf
--- /dev/null
@@ -0,0 +1,861 @@
+#include <config.h>
+#include <math.h>
+#include <pango/pangocairo.h>
+#include <gtk/gtk.h>
+
+static GtkWidget *main_window;
+static GFile *filename = NULL;
+static GtkPageSetup *page_setup = NULL;
+static GtkPrintSettings *settings = NULL;
+static gboolean file_changed = FALSE;
+static GtkTextBuffer *buffer;
+static GtkWidget *statusbar;
+static GList *active_prints = NULL;
+
+static void
+update_title (GtkWindow *window)
+{
+  char *basename;
+  char *title;
+
+  if (filename == NULL)
+    basename = g_strdup ("Untitled");
+  else
+    basename = g_file_get_basename (filename);
+
+  title = g_strdup_printf ("Simple Editor with printing - %s", basename);
+  g_free (basename);
+
+  gtk_window_set_title (window, title);
+  g_free (title);
+}
+
+static void
+update_statusbar (void)
+{
+  gchar *msg;
+  gint row, col;
+  GtkTextIter iter;
+  const char *print_str;
+
+  gtk_statusbar_pop (GTK_STATUSBAR (statusbar), 0);
+  
+  gtk_text_buffer_get_iter_at_mark (buffer,
+                                    &iter,
+                                    gtk_text_buffer_get_insert (buffer));
+
+  row = gtk_text_iter_get_line (&iter);
+  col = gtk_text_iter_get_line_offset (&iter);
+
+  print_str = "";
+  if (active_prints)
+    {
+      GtkPrintOperation *op = active_prints->data;
+      print_str = gtk_print_operation_get_status_string (op);
+    }
+  
+  msg = g_strdup_printf ("%d, %d%s %s",
+                         row, col,
+                        file_changed?" - Modified":"",
+                        print_str);
+
+  gtk_statusbar_push (GTK_STATUSBAR (statusbar), 0, msg);
+
+  g_free (msg);
+}
+
+static void
+update_ui (void)
+{
+  update_title (GTK_WINDOW (main_window));
+  update_statusbar ();
+}
+
+static char *
+get_text (void)
+{
+  GtkTextIter start, end;
+
+  gtk_text_buffer_get_start_iter (buffer, &start);
+  gtk_text_buffer_get_end_iter (buffer, &end);
+  return gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
+}
+
+static void
+set_text (const char *text,
+          gsize       len)
+{
+  gtk_text_buffer_set_text (buffer, text, len);
+  file_changed = FALSE;
+  update_ui ();
+}
+
+static void
+load_file (GFile *open_filename)
+{
+  GtkWidget *error_dialog;
+  char *contents;
+  GError *error;
+  gsize len;
+
+  error_dialog = NULL;
+  error = NULL;
+  g_file_load_contents (open_filename, NULL, &contents, &len, NULL, &error);
+  if (error == NULL)
+    {
+      if (g_utf8_validate (contents, len, NULL))
+       {
+          g_clear_object (&filename);
+         filename = g_object_ref (open_filename);
+         set_text (contents, len);
+         g_free (contents);
+       }
+      else
+       {
+          GFileInfo *info = g_file_query_info (open_filename, "standard::display-name", 0, NULL, &error);
+          const char *display_name = g_file_info_get_display_name (info);
+         error_dialog = gtk_message_dialog_new (GTK_WINDOW (main_window),
+                                                GTK_DIALOG_DESTROY_WITH_PARENT,
+                                                GTK_MESSAGE_ERROR,
+                                                GTK_BUTTONS_CLOSE,
+                                                "Error loading file %s:\n%s",
+                                                display_name,
+                                                "Not valid utf8");
+         g_object_unref (info);
+       }
+    }
+  else
+    {
+      GFileInfo *info = g_file_query_info (open_filename, "standard::display-name", 0, NULL, &error);
+      const char *display_name = g_file_info_get_display_name (info);
+      error_dialog = gtk_message_dialog_new (GTK_WINDOW (main_window),
+                                            GTK_DIALOG_DESTROY_WITH_PARENT,
+                                            GTK_MESSAGE_ERROR,
+                                            GTK_BUTTONS_CLOSE,
+                                            "Error loading file %s:\n%s",
+                                            display_name,
+                                            error->message);
+      g_object_unref (info);
+      g_error_free (error);
+    }
+
+  if (error_dialog)
+    {
+      g_signal_connect (error_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
+      gtk_widget_show (error_dialog);
+    }
+}
+
+
+static void
+save_file (GFile *save_filename)
+{
+  char *text = get_text ();
+  GtkWidget *error_dialog;
+  GError *error;
+
+  error = NULL;
+  g_file_replace_contents (save_filename,
+                           text, -1,
+                           NULL, FALSE,
+                           G_FILE_CREATE_NONE,
+                           NULL,
+                           NULL,
+                           &error);
+
+  if (error != NULL)
+    {
+      if (save_filename != filename)
+       {
+          g_clear_object (&filename);
+          filename = g_object_ref (save_filename);
+       }
+      file_changed = FALSE;
+      update_ui ();
+    }
+  else
+    {
+      GFileInfo *info = g_file_query_info (save_filename, "standard::display-name", 0, NULL, NULL);
+      const char *display_name = g_file_info_get_display_name (info);
+
+      error_dialog = gtk_message_dialog_new (GTK_WINDOW (main_window),
+                                            GTK_DIALOG_DESTROY_WITH_PARENT,
+                                            GTK_MESSAGE_ERROR,
+                                            GTK_BUTTONS_CLOSE,
+                                            "Error saving to file %s:\n%s",
+                                            display_name,
+                                            error->message);
+      
+      g_signal_connect (error_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
+      gtk_widget_show (error_dialog);
+      
+      g_error_free (error);
+      g_object_unref (info);
+    }
+}
+
+
+typedef struct {
+  char *text;
+  PangoLayout *layout;
+  GList *page_breaks;
+  GtkWidget *font_button;
+  char *font;
+} PrintData;
+
+static void
+begin_print (GtkPrintOperation *operation,
+            GtkPrintContext *context,
+            PrintData *print_data)
+{
+  PangoFontDescription *desc;
+  PangoLayoutLine *layout_line;
+  double width, height;
+  double page_height;
+  GList *page_breaks;
+  int num_lines;
+  int line;
+
+  width = gtk_print_context_get_width (context);
+  height = gtk_print_context_get_height (context);
+
+  print_data->layout = gtk_print_context_create_pango_layout (context);
+
+  desc = pango_font_description_from_string (print_data->font);
+  pango_layout_set_font_description (print_data->layout, desc);
+  pango_font_description_free (desc);
+
+  pango_layout_set_width (print_data->layout, width * PANGO_SCALE);
+  
+  pango_layout_set_text (print_data->layout, print_data->text, -1);
+
+  num_lines = pango_layout_get_line_count (print_data->layout);
+
+  page_breaks = NULL;
+  page_height = 0;
+
+  for (line = 0; line < num_lines; line++)
+    {
+      PangoRectangle ink_rect, logical_rect;
+      double line_height;
+      
+      layout_line = pango_layout_get_line (print_data->layout, line);
+      pango_layout_line_get_extents (layout_line, &ink_rect, &logical_rect);
+
+      line_height = logical_rect.height / 1024.0;
+
+      if (page_height + line_height > height)
+       {
+         page_breaks = g_list_prepend (page_breaks, GINT_TO_POINTER (line));
+         page_height = 0;
+       }
+
+      page_height += line_height;
+    }
+
+  page_breaks = g_list_reverse (page_breaks);
+  gtk_print_operation_set_n_pages (operation, g_list_length (page_breaks) + 1);
+  
+  print_data->page_breaks = page_breaks;
+}
+
+static void
+draw_page (GtkPrintOperation *operation,
+          GtkPrintContext *context,
+          int page_nr,
+          PrintData *print_data)
+{
+  cairo_t *cr;
+  GList *pagebreak;
+  int start, end, i;
+  PangoLayoutIter *iter;
+  double start_pos;
+
+  if (page_nr == 0)
+    start = 0;
+  else
+    {
+      pagebreak = g_list_nth (print_data->page_breaks, page_nr - 1);
+      start = GPOINTER_TO_INT (pagebreak->data);
+    }
+
+  pagebreak = g_list_nth (print_data->page_breaks, page_nr);
+  if (pagebreak == NULL)
+    end = pango_layout_get_line_count (print_data->layout);
+  else
+    end = GPOINTER_TO_INT (pagebreak->data);
+    
+  cr = gtk_print_context_get_cairo_context (context);
+
+  cairo_set_source_rgb (cr, 0, 0, 0);
+  
+  i = 0;
+  start_pos = 0;
+  iter = pango_layout_get_iter (print_data->layout);
+  do
+    {
+      PangoRectangle   logical_rect;
+      PangoLayoutLine *line;
+      int              baseline;
+
+      if (i >= start)
+       {
+         line = pango_layout_iter_get_line (iter);
+
+         pango_layout_iter_get_line_extents (iter, NULL, &logical_rect);
+         baseline = pango_layout_iter_get_baseline (iter);
+         
+         if (i == start)
+           start_pos = logical_rect.y / 1024.0;
+         
+         cairo_move_to (cr, logical_rect.x / 1024.0, baseline / 1024.0 - start_pos);
+         
+         pango_cairo_show_layout_line  (cr, line);
+       }
+      i++;
+    }
+  while (i < end &&
+        pango_layout_iter_next_line (iter));
+
+  pango_layout_iter_free (iter);
+}
+
+static void
+status_changed_cb (GtkPrintOperation *op,
+                  gpointer user_data)
+{
+  if (gtk_print_operation_is_finished (op))
+    {
+      active_prints = g_list_remove (active_prints, op);
+      g_object_unref (op);
+    }
+  update_statusbar ();
+}
+
+static GtkWidget *
+create_custom_widget (GtkPrintOperation *operation,
+                     PrintData *data)
+{
+  GtkWidget *vbox, *hbox, *font, *label;
+
+  gtk_print_operation_set_custom_tab_label (operation, "Other");
+  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+
+  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8);
+  gtk_box_append (GTK_BOX (vbox), hbox);
+  gtk_widget_show (hbox);
+
+  label = gtk_label_new ("Font:");
+  gtk_box_append (GTK_BOX (hbox), label);
+  gtk_widget_show (label);
+
+  font = gtk_font_button_new_with_font  (data->font);
+  gtk_box_append (GTK_BOX (hbox), font);
+  gtk_widget_show (font);
+  data->font_button = font;
+
+  return vbox;
+}
+
+static void
+custom_widget_apply (GtkPrintOperation *operation,
+                    GtkWidget *widget,
+                    PrintData *data)
+{
+  const char *selected_font;
+  selected_font = gtk_font_chooser_get_font (GTK_FONT_CHOOSER (data->font_button));
+
+  g_free (data->font);
+  data->font = g_strdup (selected_font);
+}
+
+static void
+print_done (GtkPrintOperation *op,
+           GtkPrintOperationResult res,
+           PrintData *print_data)
+{
+  GError *error = NULL;
+
+  if (res == GTK_PRINT_OPERATION_RESULT_ERROR)
+    {
+
+      GtkWidget *error_dialog;
+      
+      gtk_print_operation_get_error (op, &error);
+      
+      error_dialog = gtk_message_dialog_new (GTK_WINDOW (main_window),
+                                            GTK_DIALOG_DESTROY_WITH_PARENT,
+                                            GTK_MESSAGE_ERROR,
+                                            GTK_BUTTONS_CLOSE,
+                                            "Error printing file:\n%s",
+                                            error ? error->message : "no details");
+      g_signal_connect (error_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
+      gtk_widget_show (error_dialog);
+    }
+  else if (res == GTK_PRINT_OPERATION_RESULT_APPLY)
+    {
+      if (settings != NULL)
+       g_object_unref (settings);
+      settings = g_object_ref (gtk_print_operation_get_print_settings (op));
+    }
+
+  g_free (print_data->text);
+  g_free (print_data->font);
+  g_free (print_data);
+  
+  if (!gtk_print_operation_is_finished (op))
+    {
+      g_object_ref (op);
+      active_prints = g_list_append (active_prints, op);
+      update_statusbar ();
+      
+      /* This ref is unref:ed when we get the final state change */
+      g_signal_connect (op, "status_changed",
+                       G_CALLBACK (status_changed_cb), NULL);
+    }
+}
+
+static void
+end_print (GtkPrintOperation *op, GtkPrintContext *context, PrintData *print_data)
+{
+  g_list_free (print_data->page_breaks);
+  print_data->page_breaks = NULL;
+  g_object_unref (print_data->layout);
+  print_data->layout = NULL;
+}
+
+static void
+print_or_preview (GSimpleAction *action, GtkPrintOperationAction print_action)
+{
+  GtkPrintOperation *print;
+  PrintData *print_data;
+
+  print_data = g_new0 (PrintData, 1);
+
+  print_data->text = get_text ();
+  print_data->font = g_strdup ("Sans 12");
+
+  print = gtk_print_operation_new ();
+
+  gtk_print_operation_set_track_print_status (print, TRUE);
+
+  if (settings != NULL)
+    gtk_print_operation_set_print_settings (print, settings);
+
+  if (page_setup != NULL)
+    gtk_print_operation_set_default_page_setup (print, page_setup);
+
+  g_signal_connect (print, "begin_print", G_CALLBACK (begin_print), print_data);
+  g_signal_connect (print, "end-print", G_CALLBACK (end_print), print_data);
+  g_signal_connect (print, "draw_page", G_CALLBACK (draw_page), print_data);
+  g_signal_connect (print, "create_custom_widget", G_CALLBACK (create_custom_widget), print_data);
+  g_signal_connect (print, "custom_widget_apply", G_CALLBACK (custom_widget_apply), print_data);
+
+  g_signal_connect (print, "done", G_CALLBACK (print_done), print_data);
+
+  gtk_print_operation_set_export_filename (print, "test.pdf");
+
+#if 0
+  gtk_print_operation_set_allow_async (print, TRUE);
+#endif
+  gtk_print_operation_run (print, print_action, GTK_WINDOW (main_window), NULL);
+
+  g_object_unref (print);
+}
+
+static void
+activate_page_setup (GSimpleAction *action,
+                     GVariant      *parameter,
+                     gpointer       user_data)
+{
+  GtkPageSetup *new_page_setup;
+
+  new_page_setup = gtk_print_run_page_setup_dialog (GTK_WINDOW (main_window),
+                                                    page_setup, settings);
+
+  if (page_setup)
+    g_object_unref (page_setup);
+
+  page_setup = new_page_setup;
+}
+
+static void
+activate_print (GSimpleAction *action,
+                GVariant      *parameter,
+                gpointer       user_data)
+{
+  print_or_preview (action, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG);
+}
+
+static void
+activate_preview (GSimpleAction *action,
+                GVariant      *parameter,
+                gpointer       user_data)
+{
+  print_or_preview (action, GTK_PRINT_OPERATION_ACTION_PREVIEW);
+}
+
+static void
+on_save_response (GtkWidget *dialog,
+                  int        response)
+{
+  if (response == GTK_RESPONSE_OK)
+    {
+      GFile *save_filename = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
+      save_file (save_filename);
+      g_object_unref (save_filename);
+    }
+
+  gtk_window_destroy (GTK_WINDOW (dialog));
+}
+
+static void
+activate_save_as (GSimpleAction *action,
+                  GVariant      *parameter,
+                  gpointer       user_data)
+{
+  GtkWidget *dialog;
+
+  dialog = gtk_file_chooser_dialog_new ("Select file",
+                                        GTK_WINDOW (main_window),
+                                        GTK_FILE_CHOOSER_ACTION_SAVE,
+                                        "_Cancel", GTK_RESPONSE_CANCEL,
+                                        "_Save", GTK_RESPONSE_OK,
+                                        NULL);
+  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+  gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+  gtk_widget_show (dialog);
+
+  g_signal_connect (dialog, "response",
+                    G_CALLBACK (on_save_response),
+                    NULL);
+}
+
+static void
+activate_save (GSimpleAction *action,
+               GVariant      *parameter,
+               gpointer       user_data)
+{
+  if (filename == NULL)
+    activate_save_as (action, NULL, NULL);
+  else
+    save_file (filename);
+}
+
+static void
+on_open_response (GtkWidget *dialog,
+                  int        response)
+{
+  if (response == GTK_RESPONSE_OK)
+    {
+      GFile *open_filename = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
+      load_file (open_filename);
+      g_object_unref (open_filename);
+    }
+
+  gtk_window_destroy (GTK_WINDOW (dialog));
+}
+
+static void
+activate_open (GSimpleAction *action,
+               GVariant      *parameter,
+               gpointer       user_data)
+{
+  GtkWidget *dialog;
+
+  dialog = gtk_file_chooser_dialog_new ("Select file",
+                                        GTK_WINDOW (main_window),
+                                        GTK_FILE_CHOOSER_ACTION_OPEN,
+                                        "_Cancel", GTK_RESPONSE_CANCEL,
+                                        "_Open", GTK_RESPONSE_OK,
+                                        NULL);
+  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+  gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+  gtk_widget_show (dialog);
+
+  g_signal_connect (dialog, "response",
+                    G_CALLBACK (on_open_response),
+                    NULL);
+}
+
+static void
+activate_new (GSimpleAction *action,
+              GVariant      *parameter,
+              gpointer       user_data)
+{
+  g_free (filename);
+  filename = NULL;
+  set_text ("", 0);
+}
+
+static void
+activate_about (GSimpleAction *action,
+                GVariant      *parameter,
+                gpointer       user_data)
+{
+  const gchar *authors[] = {
+    "Alexander Larsson",
+    NULL
+  };
+  gtk_show_about_dialog (GTK_WINDOW (main_window),
+                         "name", "Print Test Editor",
+                         "logo-icon-name", "text-editor-symbolic",
+                         "version", PACKAGE_VERSION,
+                         "copyright", "© 2006-2020 Red Hat, Inc",
+                         "comments", "Program to demonstrate GTK printing.",
+                         "authors", authors,
+                         NULL);
+}
+
+static void
+activate_quit (GSimpleAction *action,
+               GVariant      *parameter,
+               gpointer       user_data)
+{
+  GtkApplication *app = user_data;
+  GtkWidget *win;
+  GList *list, *next;
+
+  list = gtk_application_get_windows (app);
+  while (list)
+    {
+      win = list->data;
+      next = list->next;
+
+      gtk_window_destroy (GTK_WINDOW (win));
+
+      list = next;
+    }
+}
+
+static GActionEntry app_entries[] = {
+  { "new", activate_new, NULL, NULL, NULL },
+  { "open", activate_open, NULL, NULL, NULL },
+  { "save", activate_save, NULL, NULL, NULL },
+  { "save-as", activate_save_as, NULL, NULL, NULL },
+  { "quit", activate_quit, NULL, NULL, NULL },
+  { "about", activate_about, NULL, NULL, NULL },
+  { "page-setup", activate_page_setup, NULL, NULL, NULL },
+  { "preview", activate_preview, NULL, NULL, NULL },
+  { "print", activate_print, NULL, NULL, NULL }
+};
+
+static const gchar ui_info[] =
+  "<interface>"
+  "  <menu id='appmenu'>"
+  "    <section>"
+  "      <item>"
+  "        <attribute name='label'>_About</attribute>"
+  "        <attribute name='action'>app.about</attribute>"
+  "        <attribute name='accel'>&lt;Primary&gt;a</attribute>"
+  "      </item>"
+  "    </section>"
+  "    <section>"
+  "      <item>"
+  "        <attribute name='label'>_Quit</attribute>"
+  "        <attribute name='action'>app.quit</attribute>"
+  "        <attribute name='accel'>&lt;Primary&gt;q</attribute>"
+  "      </item>"
+  "    </section>"
+  "  </menu>"
+  "  <menu id='menubar'>"
+  "    <submenu>"
+  "      <attribute name='label'>_File</attribute>"
+  "      <section>"
+  "        <item>"
+  "          <attribute name='label'>_New</attribute>"
+  "          <attribute name='action'>app.new</attribute>"
+  "          <attribute name='accel'>&lt;Primary&gt;n</attribute>"
+  "        </item>"
+  "        <item>"
+  "          <attribute name='label'>_Open</attribute>"
+  "          <attribute name='action'>app.open</attribute>"
+  "        </item>"
+  "        <item>"
+  "          <attribute name='label'>_Save</attribute>"
+  "          <attribute name='action'>app.save</attribute>"
+  "          <attribute name='accel'>&lt;Primary&gt;s</attribute>"
+  "        </item>"
+  "        <item>"
+  "          <attribute name='label'>Save _As...</attribute>"
+  "          <attribute name='action'>app.save-as</attribute>"
+  "          <attribute name='accel'>&lt;Primary&gt;s</attribute>"
+  "        </item>"
+  "      </section>"
+  "      <section>"
+  "        <item>"
+  "          <attribute name='label'>Page Setup</attribute>"
+  "          <attribute name='action'>app.page-setup</attribute>"
+  "        </item>"
+  "        <item>"
+  "          <attribute name='label'>Preview</attribute>"
+  "          <attribute name='action'>app.preview</attribute>"
+  "        </item>"
+  "        <item>"
+  "          <attribute name='label'>Print</attribute>"
+  "          <attribute name='action'>app.print</attribute>"
+  "        </item>"
+  "      </section>"
+  "    </submenu>"
+  "  </menu>"
+  "</interface>";
+
+static void
+buffer_changed_callback (GtkTextBuffer *text_buffer)
+{
+  file_changed = TRUE;
+  update_statusbar ();
+}
+
+static void
+mark_set_callback (GtkTextBuffer     *text_buffer,
+                   const GtkTextIter *new_location,
+                   GtkTextMark       *mark,
+                   gpointer           data)
+{
+  update_statusbar ();
+}
+
+static gint
+command_line (GApplication            *application,
+              GApplicationCommandLine *command_line)
+{
+  int argc;
+  char **argv;
+
+  argv = g_application_command_line_get_arguments (command_line, &argc);
+
+  if (argc == 2)
+    {
+      GFile *file = g_file_new_for_commandline_arg (argv[1]);
+      load_file (file);
+      g_object_unref (file);
+    }
+
+  return 0;
+}
+
+static void
+startup (GApplication *app)
+{
+  GtkBuilder *builder;
+  GMenuModel *appmenu;
+  GMenuModel *menubar;
+
+  builder = gtk_builder_new ();
+  gtk_builder_add_from_string (builder, ui_info, -1, NULL);
+
+  appmenu = (GMenuModel *)gtk_builder_get_object (builder, "appmenu");
+  menubar = (GMenuModel *)gtk_builder_get_object (builder, "menubar");
+
+  gtk_application_set_app_menu (GTK_APPLICATION (app), appmenu);
+  gtk_application_set_menubar (GTK_APPLICATION (app), menubar);
+
+  g_object_unref (builder);
+}
+
+static void
+activate (GApplication *app)
+{
+  GtkWidget *box;
+  GtkWidget *sw;
+  GtkWidget *contents;
+
+  main_window = gtk_application_window_new (GTK_APPLICATION (app));
+  gtk_window_set_icon_name (GTK_WINDOW (main_window), "text-editor");
+  gtk_window_set_default_size (GTK_WINDOW (main_window), 400, 600);
+  gtk_application_window_set_show_menubar (GTK_APPLICATION_WINDOW (main_window), TRUE);
+  update_title (GTK_WINDOW (main_window));
+
+  box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+  gtk_window_set_child (GTK_WINDOW (main_window), box);
+
+  /* Create document  */
+  sw = gtk_scrolled_window_new (NULL, NULL);
+
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+                                  GTK_POLICY_AUTOMATIC,
+                                  GTK_POLICY_AUTOMATIC);
+
+  gtk_scrolled_window_set_has_frame (GTK_SCROLLED_WINDOW (sw), TRUE);
+
+  gtk_widget_set_vexpand (sw, TRUE);
+  gtk_box_append (GTK_BOX (box), sw);
+
+  contents = gtk_text_view_new ();
+  gtk_widget_grab_focus (contents);
+
+  gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw),
+                     contents);
+
+  /* Create statusbar */
+  statusbar = gtk_statusbar_new ();
+  gtk_box_append (GTK_BOX (box), statusbar);
+
+  /* Show text widget info in the statusbar */
+  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (contents));
+
+  g_signal_connect_object (buffer,
+                           "changed",
+                           G_CALLBACK (buffer_changed_callback),
+                           NULL,
+                           0);
+
+  g_signal_connect_object (buffer,
+                           "mark_set", /* cursor moved */
+                           G_CALLBACK (mark_set_callback),
+                           NULL,
+                           0);
+
+  update_ui ();
+
+  gtk_widget_show (main_window);
+}
+
+int
+main (int argc, char **argv)
+{
+  GtkApplication *app;
+  GError *error = NULL;
+
+  gtk_init ();
+
+  settings = gtk_print_settings_new_from_file ("print-settings.ini", &error);
+  if (error) {
+    g_print ("Failed to load print settings: %s\n", error->message);
+    g_clear_error (&error);
+
+    settings = gtk_print_settings_new ();
+  }
+  g_assert (settings != NULL);
+
+  page_setup = gtk_page_setup_new_from_file ("page-setup.ini", &error);
+  if (error) {
+    g_print ("Failed to load page setup: %s\n", error->message);
+    g_clear_error (&error);
+  }
+
+  app = gtk_application_new ("org.gtk.PrintEditor", 0);
+
+  g_action_map_add_action_entries (G_ACTION_MAP (app),
+                                   app_entries, G_N_ELEMENTS (app_entries),
+                                   app);
+
+  g_signal_connect (app, "startup", G_CALLBACK (startup), NULL);
+  g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
+  g_signal_connect (app, "command-line", G_CALLBACK (command_line), NULL);
+
+  g_application_run (G_APPLICATION (app), argc, argv);
+
+  if (!gtk_print_settings_to_file (settings, "print-settings.ini", &error)) {
+    g_print ("Failed to save print settings: %s\n", error->message);
+    g_clear_error (&error);
+  }
+  if (page_setup &&
+      !gtk_page_setup_to_file (page_setup, "page-setup.ini", &error)) {
+    g_print ("Failed to save page setup: %s\n", error->message);
+    g_clear_error (&error);
+  }
+
+  return 0;
+}
index facebb3f0153e71ef06447942f820875ba301019..1e7c504fcdda9b4b1e0447472cf774015efe72ad 100644 (file)
@@ -10,7 +10,6 @@ gtk_tests = [
   ['scrolling-performance', ['frame-stats.c', 'variable.c']],
   ['blur-performance', ['../gsk/gskcairoblur.c']],
   ['simple'],
-  ['print-editor'],
   ['video-timer', ['variable.c']],
   ['testaccel'],
   ['testadjustsize'],
diff --git a/tests/print-editor.c b/tests/print-editor.c
deleted file mode 100644 (file)
index 37ea8cf..0000000
+++ /dev/null
@@ -1,861 +0,0 @@
-#include <config.h>
-#include <math.h>
-#include <pango/pangocairo.h>
-#include <gtk/gtk.h>
-
-static GtkWidget *main_window;
-static GFile *filename = NULL;
-static GtkPageSetup *page_setup = NULL;
-static GtkPrintSettings *settings = NULL;
-static gboolean file_changed = FALSE;
-static GtkTextBuffer *buffer;
-static GtkWidget *statusbar;
-static GList *active_prints = NULL;
-
-static void
-update_title (GtkWindow *window)
-{
-  char *basename;
-  char *title;
-
-  if (filename == NULL)
-    basename = g_strdup ("Untitled");
-  else
-    basename = g_file_get_basename (filename);
-
-  title = g_strdup_printf ("Simple Editor with printing - %s", basename);
-  g_free (basename);
-
-  gtk_window_set_title (window, title);
-  g_free (title);
-}
-
-static void
-update_statusbar (void)
-{
-  gchar *msg;
-  gint row, col;
-  GtkTextIter iter;
-  const char *print_str;
-
-  gtk_statusbar_pop (GTK_STATUSBAR (statusbar), 0);
-  
-  gtk_text_buffer_get_iter_at_mark (buffer,
-                                    &iter,
-                                    gtk_text_buffer_get_insert (buffer));
-
-  row = gtk_text_iter_get_line (&iter);
-  col = gtk_text_iter_get_line_offset (&iter);
-
-  print_str = "";
-  if (active_prints)
-    {
-      GtkPrintOperation *op = active_prints->data;
-      print_str = gtk_print_operation_get_status_string (op);
-    }
-  
-  msg = g_strdup_printf ("%d, %d%s %s",
-                         row, col,
-                        file_changed?" - Modified":"",
-                        print_str);
-
-  gtk_statusbar_push (GTK_STATUSBAR (statusbar), 0, msg);
-
-  g_free (msg);
-}
-
-static void
-update_ui (void)
-{
-  update_title (GTK_WINDOW (main_window));
-  update_statusbar ();
-}
-
-static char *
-get_text (void)
-{
-  GtkTextIter start, end;
-
-  gtk_text_buffer_get_start_iter (buffer, &start);
-  gtk_text_buffer_get_end_iter (buffer, &end);
-  return gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
-}
-
-static void
-set_text (const char *text,
-          gsize       len)
-{
-  gtk_text_buffer_set_text (buffer, text, len);
-  file_changed = FALSE;
-  update_ui ();
-}
-
-static void
-load_file (GFile *open_filename)
-{
-  GtkWidget *error_dialog;
-  char *contents;
-  GError *error;
-  gsize len;
-
-  error_dialog = NULL;
-  error = NULL;
-  g_file_load_contents (open_filename, NULL, &contents, &len, NULL, &error);
-  if (error == NULL)
-    {
-      if (g_utf8_validate (contents, len, NULL))
-       {
-          g_clear_object (&filename);
-         filename = g_object_ref (open_filename);
-         set_text (contents, len);
-         g_free (contents);
-       }
-      else
-       {
-          GFileInfo *info = g_file_query_info (open_filename, "standard::display-name", 0, NULL, &error);
-          const char *display_name = g_file_info_get_display_name (info);
-         error_dialog = gtk_message_dialog_new (GTK_WINDOW (main_window),
-                                                GTK_DIALOG_DESTROY_WITH_PARENT,
-                                                GTK_MESSAGE_ERROR,
-                                                GTK_BUTTONS_CLOSE,
-                                                "Error loading file %s:\n%s",
-                                                display_name,
-                                                "Not valid utf8");
-         g_object_unref (info);
-       }
-    }
-  else
-    {
-      GFileInfo *info = g_file_query_info (open_filename, "standard::display-name", 0, NULL, &error);
-      const char *display_name = g_file_info_get_display_name (info);
-      error_dialog = gtk_message_dialog_new (GTK_WINDOW (main_window),
-                                            GTK_DIALOG_DESTROY_WITH_PARENT,
-                                            GTK_MESSAGE_ERROR,
-                                            GTK_BUTTONS_CLOSE,
-                                            "Error loading file %s:\n%s",
-                                            display_name,
-                                            error->message);
-      g_object_unref (info);
-      g_error_free (error);
-    }
-
-  if (error_dialog)
-    {
-      g_signal_connect (error_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
-      gtk_widget_show (error_dialog);
-    }
-}
-
-
-static void
-save_file (GFile *save_filename)
-{
-  char *text = get_text ();
-  GtkWidget *error_dialog;
-  GError *error;
-
-  error = NULL;
-  g_file_replace_contents (save_filename,
-                           text, -1,
-                           NULL, FALSE,
-                           G_FILE_CREATE_NONE,
-                           NULL,
-                           NULL,
-                           &error);
-
-  if (error != NULL)
-    {
-      if (save_filename != filename)
-       {
-          g_clear_object (&filename);
-          filename = g_object_ref (save_filename);
-       }
-      file_changed = FALSE;
-      update_ui ();
-    }
-  else
-    {
-      GFileInfo *info = g_file_query_info (save_filename, "standard::display-name", 0, NULL, NULL);
-      const char *display_name = g_file_info_get_display_name (info);
-
-      error_dialog = gtk_message_dialog_new (GTK_WINDOW (main_window),
-                                            GTK_DIALOG_DESTROY_WITH_PARENT,
-                                            GTK_MESSAGE_ERROR,
-                                            GTK_BUTTONS_CLOSE,
-                                            "Error saving to file %s:\n%s",
-                                            display_name,
-                                            error->message);
-      
-      g_signal_connect (error_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
-      gtk_widget_show (error_dialog);
-      
-      g_error_free (error);
-      g_object_unref (info);
-    }
-}
-
-
-typedef struct {
-  char *text;
-  PangoLayout *layout;
-  GList *page_breaks;
-  GtkWidget *font_button;
-  char *font;
-} PrintData;
-
-static void
-begin_print (GtkPrintOperation *operation,
-            GtkPrintContext *context,
-            PrintData *print_data)
-{
-  PangoFontDescription *desc;
-  PangoLayoutLine *layout_line;
-  double width, height;
-  double page_height;
-  GList *page_breaks;
-  int num_lines;
-  int line;
-
-  width = gtk_print_context_get_width (context);
-  height = gtk_print_context_get_height (context);
-
-  print_data->layout = gtk_print_context_create_pango_layout (context);
-
-  desc = pango_font_description_from_string (print_data->font);
-  pango_layout_set_font_description (print_data->layout, desc);
-  pango_font_description_free (desc);
-
-  pango_layout_set_width (print_data->layout, width * PANGO_SCALE);
-  
-  pango_layout_set_text (print_data->layout, print_data->text, -1);
-
-  num_lines = pango_layout_get_line_count (print_data->layout);
-
-  page_breaks = NULL;
-  page_height = 0;
-
-  for (line = 0; line < num_lines; line++)
-    {
-      PangoRectangle ink_rect, logical_rect;
-      double line_height;
-      
-      layout_line = pango_layout_get_line (print_data->layout, line);
-      pango_layout_line_get_extents (layout_line, &ink_rect, &logical_rect);
-
-      line_height = logical_rect.height / 1024.0;
-
-      if (page_height + line_height > height)
-       {
-         page_breaks = g_list_prepend (page_breaks, GINT_TO_POINTER (line));
-         page_height = 0;
-       }
-
-      page_height += line_height;
-    }
-
-  page_breaks = g_list_reverse (page_breaks);
-  gtk_print_operation_set_n_pages (operation, g_list_length (page_breaks) + 1);
-  
-  print_data->page_breaks = page_breaks;
-}
-
-static void
-draw_page (GtkPrintOperation *operation,
-          GtkPrintContext *context,
-          int page_nr,
-          PrintData *print_data)
-{
-  cairo_t *cr;
-  GList *pagebreak;
-  int start, end, i;
-  PangoLayoutIter *iter;
-  double start_pos;
-
-  if (page_nr == 0)
-    start = 0;
-  else
-    {
-      pagebreak = g_list_nth (print_data->page_breaks, page_nr - 1);
-      start = GPOINTER_TO_INT (pagebreak->data);
-    }
-
-  pagebreak = g_list_nth (print_data->page_breaks, page_nr);
-  if (pagebreak == NULL)
-    end = pango_layout_get_line_count (print_data->layout);
-  else
-    end = GPOINTER_TO_INT (pagebreak->data);
-    
-  cr = gtk_print_context_get_cairo_context (context);
-
-  cairo_set_source_rgb (cr, 0, 0, 0);
-  
-  i = 0;
-  start_pos = 0;
-  iter = pango_layout_get_iter (print_data->layout);
-  do
-    {
-      PangoRectangle   logical_rect;
-      PangoLayoutLine *line;
-      int              baseline;
-
-      if (i >= start)
-       {
-         line = pango_layout_iter_get_line (iter);
-
-         pango_layout_iter_get_line_extents (iter, NULL, &logical_rect);
-         baseline = pango_layout_iter_get_baseline (iter);
-         
-         if (i == start)
-           start_pos = logical_rect.y / 1024.0;
-         
-         cairo_move_to (cr, logical_rect.x / 1024.0, baseline / 1024.0 - start_pos);
-         
-         pango_cairo_show_layout_line  (cr, line);
-       }
-      i++;
-    }
-  while (i < end &&
-        pango_layout_iter_next_line (iter));
-
-  pango_layout_iter_free (iter);
-}
-
-static void
-status_changed_cb (GtkPrintOperation *op,
-                  gpointer user_data)
-{
-  if (gtk_print_operation_is_finished (op))
-    {
-      active_prints = g_list_remove (active_prints, op);
-      g_object_unref (op);
-    }
-  update_statusbar ();
-}
-
-static GtkWidget *
-create_custom_widget (GtkPrintOperation *operation,
-                     PrintData *data)
-{
-  GtkWidget *vbox, *hbox, *font, *label;
-
-  gtk_print_operation_set_custom_tab_label (operation, "Other");
-  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
-
-  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8);
-  gtk_box_append (GTK_BOX (vbox), hbox);
-  gtk_widget_show (hbox);
-
-  label = gtk_label_new ("Font:");
-  gtk_box_append (GTK_BOX (hbox), label);
-  gtk_widget_show (label);
-
-  font = gtk_font_button_new_with_font  (data->font);
-  gtk_box_append (GTK_BOX (hbox), font);
-  gtk_widget_show (font);
-  data->font_button = font;
-
-  return vbox;
-}
-
-static void
-custom_widget_apply (GtkPrintOperation *operation,
-                    GtkWidget *widget,
-                    PrintData *data)
-{
-  const char *selected_font;
-  selected_font = gtk_font_chooser_get_font (GTK_FONT_CHOOSER (data->font_button));
-
-  g_free (data->font);
-  data->font = g_strdup (selected_font);
-}
-
-static void
-print_done (GtkPrintOperation *op,
-           GtkPrintOperationResult res,
-           PrintData *print_data)
-{
-  GError *error = NULL;
-
-  if (res == GTK_PRINT_OPERATION_RESULT_ERROR)
-    {
-
-      GtkWidget *error_dialog;
-      
-      gtk_print_operation_get_error (op, &error);
-      
-      error_dialog = gtk_message_dialog_new (GTK_WINDOW (main_window),
-                                            GTK_DIALOG_DESTROY_WITH_PARENT,
-                                            GTK_MESSAGE_ERROR,
-                                            GTK_BUTTONS_CLOSE,
-                                            "Error printing file:\n%s",
-                                            error ? error->message : "no details");
-      g_signal_connect (error_dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
-      gtk_widget_show (error_dialog);
-    }
-  else if (res == GTK_PRINT_OPERATION_RESULT_APPLY)
-    {
-      if (settings != NULL)
-       g_object_unref (settings);
-      settings = g_object_ref (gtk_print_operation_get_print_settings (op));
-    }
-
-  g_free (print_data->text);
-  g_free (print_data->font);
-  g_free (print_data);
-  
-  if (!gtk_print_operation_is_finished (op))
-    {
-      g_object_ref (op);
-      active_prints = g_list_append (active_prints, op);
-      update_statusbar ();
-      
-      /* This ref is unref:ed when we get the final state change */
-      g_signal_connect (op, "status_changed",
-                       G_CALLBACK (status_changed_cb), NULL);
-    }
-}
-
-static void
-end_print (GtkPrintOperation *op, GtkPrintContext *context, PrintData *print_data)
-{
-  g_list_free (print_data->page_breaks);
-  print_data->page_breaks = NULL;
-  g_object_unref (print_data->layout);
-  print_data->layout = NULL;
-}
-
-static void
-print_or_preview (GSimpleAction *action, GtkPrintOperationAction print_action)
-{
-  GtkPrintOperation *print;
-  PrintData *print_data;
-
-  print_data = g_new0 (PrintData, 1);
-
-  print_data->text = get_text ();
-  print_data->font = g_strdup ("Sans 12");
-
-  print = gtk_print_operation_new ();
-
-  gtk_print_operation_set_track_print_status (print, TRUE);
-
-  if (settings != NULL)
-    gtk_print_operation_set_print_settings (print, settings);
-
-  if (page_setup != NULL)
-    gtk_print_operation_set_default_page_setup (print, page_setup);
-
-  g_signal_connect (print, "begin_print", G_CALLBACK (begin_print), print_data);
-  g_signal_connect (print, "end-print", G_CALLBACK (end_print), print_data);
-  g_signal_connect (print, "draw_page", G_CALLBACK (draw_page), print_data);
-  g_signal_connect (print, "create_custom_widget", G_CALLBACK (create_custom_widget), print_data);
-  g_signal_connect (print, "custom_widget_apply", G_CALLBACK (custom_widget_apply), print_data);
-
-  g_signal_connect (print, "done", G_CALLBACK (print_done), print_data);
-
-  gtk_print_operation_set_export_filename (print, "test.pdf");
-
-#if 0
-  gtk_print_operation_set_allow_async (print, TRUE);
-#endif
-  gtk_print_operation_run (print, print_action, GTK_WINDOW (main_window), NULL);
-
-  g_object_unref (print);
-}
-
-static void
-activate_page_setup (GSimpleAction *action,
-                     GVariant      *parameter,
-                     gpointer       user_data)
-{
-  GtkPageSetup *new_page_setup;
-
-  new_page_setup = gtk_print_run_page_setup_dialog (GTK_WINDOW (main_window),
-                                                    page_setup, settings);
-
-  if (page_setup)
-    g_object_unref (page_setup);
-
-  page_setup = new_page_setup;
-}
-
-static void
-activate_print (GSimpleAction *action,
-                GVariant      *parameter,
-                gpointer       user_data)
-{
-  print_or_preview (action, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG);
-}
-
-static void
-activate_preview (GSimpleAction *action,
-                GVariant      *parameter,
-                gpointer       user_data)
-{
-  print_or_preview (action, GTK_PRINT_OPERATION_ACTION_PREVIEW);
-}
-
-static void
-on_save_response (GtkWidget *dialog,
-                  int        response)
-{
-  if (response == GTK_RESPONSE_OK)
-    {
-      GFile *save_filename = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
-      save_file (save_filename);
-      g_object_unref (save_filename);
-    }
-
-  gtk_window_destroy (GTK_WINDOW (dialog));
-}
-
-static void
-activate_save_as (GSimpleAction *action,
-                  GVariant      *parameter,
-                  gpointer       user_data)
-{
-  GtkWidget *dialog;
-
-  dialog = gtk_file_chooser_dialog_new ("Select file",
-                                        GTK_WINDOW (main_window),
-                                        GTK_FILE_CHOOSER_ACTION_SAVE,
-                                        "_Cancel", GTK_RESPONSE_CANCEL,
-                                        "_Save", GTK_RESPONSE_OK,
-                                        NULL);
-  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
-  gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
-  gtk_widget_show (dialog);
-
-  g_signal_connect (dialog, "response",
-                    G_CALLBACK (on_save_response),
-                    NULL);
-}
-
-static void
-activate_save (GSimpleAction *action,
-               GVariant      *parameter,
-               gpointer       user_data)
-{
-  if (filename == NULL)
-    activate_save_as (action, NULL, NULL);
-  else
-    save_file (filename);
-}
-
-static void
-on_open_response (GtkWidget *dialog,
-                  int        response)
-{
-  if (response == GTK_RESPONSE_OK)
-    {
-      GFile *open_filename = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
-      load_file (open_filename);
-      g_object_unref (open_filename);
-    }
-
-  gtk_window_destroy (GTK_WINDOW (dialog));
-}
-
-static void
-activate_open (GSimpleAction *action,
-               GVariant      *parameter,
-               gpointer       user_data)
-{
-  GtkWidget *dialog;
-
-  dialog = gtk_file_chooser_dialog_new ("Select file",
-                                        GTK_WINDOW (main_window),
-                                        GTK_FILE_CHOOSER_ACTION_OPEN,
-                                        "_Cancel", GTK_RESPONSE_CANCEL,
-                                        "_Open", GTK_RESPONSE_OK,
-                                        NULL);
-  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
-  gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
-  gtk_widget_show (dialog);
-
-  g_signal_connect (dialog, "response",
-                    G_CALLBACK (on_open_response),
-                    NULL);
-}
-
-static void
-activate_new (GSimpleAction *action,
-              GVariant      *parameter,
-              gpointer       user_data)
-{
-  g_free (filename);
-  filename = NULL;
-  set_text ("", 0);
-}
-
-static void
-activate_about (GSimpleAction *action,
-                GVariant      *parameter,
-                gpointer       user_data)
-{
-  const gchar *authors[] = {
-    "Alexander Larsson",
-    NULL
-  };
-  gtk_show_about_dialog (GTK_WINDOW (main_window),
-                         "name", "Print Test Editor",
-                         "logo-icon-name", "text-editor-symbolic",
-                         "version", PACKAGE_VERSION,
-                         "copyright", "© 2006-2020 Red Hat, Inc",
-                         "comments", "Program to demonstrate GTK printing.",
-                         "authors", authors,
-                         NULL);
-}
-
-static void
-activate_quit (GSimpleAction *action,
-               GVariant      *parameter,
-               gpointer       user_data)
-{
-  GtkApplication *app = user_data;
-  GtkWidget *win;
-  GList *list, *next;
-
-  list = gtk_application_get_windows (app);
-  while (list)
-    {
-      win = list->data;
-      next = list->next;
-
-      gtk_window_destroy (GTK_WINDOW (win));
-
-      list = next;
-    }
-}
-
-static GActionEntry app_entries[] = {
-  { "new", activate_new, NULL, NULL, NULL },
-  { "open", activate_open, NULL, NULL, NULL },
-  { "save", activate_save, NULL, NULL, NULL },
-  { "save-as", activate_save_as, NULL, NULL, NULL },
-  { "quit", activate_quit, NULL, NULL, NULL },
-  { "about", activate_about, NULL, NULL, NULL },
-  { "page-setup", activate_page_setup, NULL, NULL, NULL },
-  { "preview", activate_preview, NULL, NULL, NULL },
-  { "print", activate_print, NULL, NULL, NULL }
-};
-
-static const gchar ui_info[] =
-  "<interface>"
-  "  <menu id='appmenu'>"
-  "    <section>"
-  "      <item>"
-  "        <attribute name='label'>_About</attribute>"
-  "        <attribute name='action'>app.about</attribute>"
-  "        <attribute name='accel'>&lt;Primary&gt;a</attribute>"
-  "      </item>"
-  "    </section>"
-  "    <section>"
-  "      <item>"
-  "        <attribute name='label'>_Quit</attribute>"
-  "        <attribute name='action'>app.quit</attribute>"
-  "        <attribute name='accel'>&lt;Primary&gt;q</attribute>"
-  "      </item>"
-  "    </section>"
-  "  </menu>"
-  "  <menu id='menubar'>"
-  "    <submenu>"
-  "      <attribute name='label'>_File</attribute>"
-  "      <section>"
-  "        <item>"
-  "          <attribute name='label'>_New</attribute>"
-  "          <attribute name='action'>app.new</attribute>"
-  "          <attribute name='accel'>&lt;Primary&gt;n</attribute>"
-  "        </item>"
-  "        <item>"
-  "          <attribute name='label'>_Open</attribute>"
-  "          <attribute name='action'>app.open</attribute>"
-  "        </item>"
-  "        <item>"
-  "          <attribute name='label'>_Save</attribute>"
-  "          <attribute name='action'>app.save</attribute>"
-  "          <attribute name='accel'>&lt;Primary&gt;s</attribute>"
-  "        </item>"
-  "        <item>"
-  "          <attribute name='label'>Save _As...</attribute>"
-  "          <attribute name='action'>app.save-as</attribute>"
-  "          <attribute name='accel'>&lt;Primary&gt;s</attribute>"
-  "        </item>"
-  "      </section>"
-  "      <section>"
-  "        <item>"
-  "          <attribute name='label'>Page Setup</attribute>"
-  "          <attribute name='action'>app.page-setup</attribute>"
-  "        </item>"
-  "        <item>"
-  "          <attribute name='label'>Preview</attribute>"
-  "          <attribute name='action'>app.preview</attribute>"
-  "        </item>"
-  "        <item>"
-  "          <attribute name='label'>Print</attribute>"
-  "          <attribute name='action'>app.print</attribute>"
-  "        </item>"
-  "      </section>"
-  "    </submenu>"
-  "  </menu>"
-  "</interface>";
-
-static void
-buffer_changed_callback (GtkTextBuffer *text_buffer)
-{
-  file_changed = TRUE;
-  update_statusbar ();
-}
-
-static void
-mark_set_callback (GtkTextBuffer     *text_buffer,
-                   const GtkTextIter *new_location,
-                   GtkTextMark       *mark,
-                   gpointer           data)
-{
-  update_statusbar ();
-}
-
-static gint
-command_line (GApplication            *application,
-              GApplicationCommandLine *command_line)
-{
-  int argc;
-  char **argv;
-
-  argv = g_application_command_line_get_arguments (command_line, &argc);
-
-  if (argc == 2)
-    {
-      GFile *file = g_file_new_for_commandline_arg (argv[1]);
-      load_file (file);
-      g_object_unref (file);
-    }
-
-  return 0;
-}
-
-static void
-startup (GApplication *app)
-{
-  GtkBuilder *builder;
-  GMenuModel *appmenu;
-  GMenuModel *menubar;
-
-  builder = gtk_builder_new ();
-  gtk_builder_add_from_string (builder, ui_info, -1, NULL);
-
-  appmenu = (GMenuModel *)gtk_builder_get_object (builder, "appmenu");
-  menubar = (GMenuModel *)gtk_builder_get_object (builder, "menubar");
-
-  gtk_application_set_app_menu (GTK_APPLICATION (app), appmenu);
-  gtk_application_set_menubar (GTK_APPLICATION (app), menubar);
-
-  g_object_unref (builder);
-}
-
-static void
-activate (GApplication *app)
-{
-  GtkWidget *box;
-  GtkWidget *sw;
-  GtkWidget *contents;
-
-  main_window = gtk_application_window_new (GTK_APPLICATION (app));
-  gtk_window_set_icon_name (GTK_WINDOW (main_window), "text-editor");
-  gtk_window_set_default_size (GTK_WINDOW (main_window), 400, 600);
-  gtk_application_window_set_show_menubar (GTK_APPLICATION_WINDOW (main_window), TRUE);
-  update_title (GTK_WINDOW (main_window));
-
-  box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
-  gtk_window_set_child (GTK_WINDOW (main_window), box);
-
-  /* Create document  */
-  sw = gtk_scrolled_window_new (NULL, NULL);
-
-  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
-                                  GTK_POLICY_AUTOMATIC,
-                                  GTK_POLICY_AUTOMATIC);
-
-  gtk_scrolled_window_set_has_frame (GTK_SCROLLED_WINDOW (sw), TRUE);
-
-  gtk_widget_set_vexpand (sw, TRUE);
-  gtk_box_append (GTK_BOX (box), sw);
-
-  contents = gtk_text_view_new ();
-  gtk_widget_grab_focus (contents);
-
-  gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw),
-                     contents);
-
-  /* Create statusbar */
-  statusbar = gtk_statusbar_new ();
-  gtk_box_append (GTK_BOX (box), statusbar);
-
-  /* Show text widget info in the statusbar */
-  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (contents));
-
-  g_signal_connect_object (buffer,
-                           "changed",
-                           G_CALLBACK (buffer_changed_callback),
-                           NULL,
-                           0);
-
-  g_signal_connect_object (buffer,
-                           "mark_set", /* cursor moved */
-                           G_CALLBACK (mark_set_callback),
-                           NULL,
-                           0);
-
-  update_ui ();
-
-  gtk_widget_show (main_window);
-}
-
-int
-main (int argc, char **argv)
-{
-  GtkApplication *app;
-  GError *error = NULL;
-
-  gtk_init ();
-
-  settings = gtk_print_settings_new_from_file ("print-settings.ini", &error);
-  if (error) {
-    g_print ("Failed to load print settings: %s\n", error->message);
-    g_clear_error (&error);
-
-    settings = gtk_print_settings_new ();
-  }
-  g_assert (settings != NULL);
-
-  page_setup = gtk_page_setup_new_from_file ("page-setup.ini", &error);
-  if (error) {
-    g_print ("Failed to load page setup: %s\n", error->message);
-    g_clear_error (&error);
-  }
-
-  app = gtk_application_new ("org.gtk.PrintEditor", 0);
-
-  g_action_map_add_action_entries (G_ACTION_MAP (app),
-                                   app_entries, G_N_ELEMENTS (app_entries),
-                                   app);
-
-  g_signal_connect (app, "startup", G_CALLBACK (startup), NULL);
-  g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
-  g_signal_connect (app, "command-line", G_CALLBACK (command_line), NULL);
-
-  g_application_run (G_APPLICATION (app), argc, argv);
-
-  if (!gtk_print_settings_to_file (settings, "print-settings.ini", &error)) {
-    g_print ("Failed to save print settings: %s\n", error->message);
-    g_clear_error (&error);
-  }
-  if (page_setup &&
-      !gtk_page_setup_to_file (page_setup, "page-setup.ini", &error)) {
-    g_print ("Failed to save page setup: %s\n", error->message);
-    g_clear_error (&error);
-  }
-
-  return 0;
-}